package comcksys.authserver.config.convert;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.util.IOUtils;
import com.alibaba.fastjson.util.TypeUtils;
import comcksys.authserver.service.impl.MyUserDetailsImpl;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

import java.nio.charset.StandardCharsets;

/**
 * @author: yxzhang23
 * @time: 2021/4/9
 */
public class MyTokenStoreSerializationStrategy implements RedisTokenStoreSerializationStrategy {

    private final static ParserConfig DEFAULT_REDIS_CONFIG = new ParserConfig();

    static {
        //设置Fastjson Json自动转换为Java对象
        DEFAULT_REDIS_CONFIG.setAutoTypeSupport(true);

        //自定义oauth2序列化：DefaultOAuth2RefreshToken 没有setValue方法，会导致JSON序列化为null
        DEFAULT_REDIS_CONFIG.putDeserializer(DefaultOAuth2RefreshToken.class, new DefaultOauth2RefreshTokenSerializer());
        DEFAULT_REDIS_CONFIG.putDeserializer(OAuth2Authentication.class, new Oauth2AuthenticationSerializer());

        DEFAULT_REDIS_CONFIG.addAccept("org.springframework.security.oauth2.provider.");
        DEFAULT_REDIS_CONFIG.addAccept("org.springframework.security.oauth2.provider.client");
        TypeUtils.addMapping("org.springframework.security.oauth2.provider.OAuth2Authentication",
                OAuth2Authentication.class);
        TypeUtils.addMapping("org.springframework.security.oauth2.provider.client.BaseClientDetails",
                BaseClientDetails.class);

        DEFAULT_REDIS_CONFIG.addAccept("org.springframework.security.oauth2.common.");
        TypeUtils.addMapping("org.springframework.security.oauth2.common.DefaultOAuth2AccessToken",
                DefaultOAuth2AccessToken.class);
        TypeUtils.addMapping("org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken",
                DefaultExpiringOAuth2RefreshToken.class);

        DEFAULT_REDIS_CONFIG.addAccept("org.springframework.security.web.authentication.preauth");
        TypeUtils.addMapping("org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken",
                PreAuthenticatedAuthenticationToken.class);

        DEFAULT_REDIS_CONFIG.addAccept("comcksys.authserver.service.impl");
        TypeUtils.addMapping("comcksys.authserver.service.impl.MyUserDetailsImpl", MyUserDetailsImpl.class);
    }

    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) {
        assert clazz != null : "clazz not null";
        if (bytes == null || bytes.length == 0) {
            return null;
        }

        try {
            return JSON.parseObject(new String(bytes, IOUtils.UTF8), clazz, DEFAULT_REDIS_CONFIG);
        } catch (Exception ex) {
            throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
        }
    }

    @Override
    public String deserializeString(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        return new String(bytes, IOUtils.UTF8);
    }

    @Override
    public byte[] serialize(Object object) {
        if (object == null) {
            return new byte[0];
        }

        try {
            byte[] bytes = JSON.toJSONBytes(object, SerializerFeature.WriteClassName,
                    SerializerFeature.DisableCircularReferenceDetect);
            return bytes;
        } catch (Exception ex) {
            throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
        }
    }

    @Override
    public byte[] serialize(String data) {
        if (data == null || data.length() == 0) {
            return new byte[0];
        }

        return data.getBytes(StandardCharsets.UTF_8);
    }
}
